package com.uxsino.simo.incremental.parser;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.uxsino.commons.model.InterfaceStatus;
import com.uxsino.commons.utils.ArithUtils;
import com.uxsino.reactorq.model.IndicatorValue;
import com.uxsino.simo.incremental.IncrementalParser;

public class IfEntryParser implements IncrementalParser {

    private static final Logger logger = LoggerFactory.getLogger(IfEntryParser.class);

    // 计算允许最小时间间隔 (1分钟)
    private final static int MIN_INTERVAL = 60;

    private final static int SIZE = 8;

    private final static int PERCENT = 100;

    private final static long MAXVALUE_32 = 4294967295L;

    @Override
    public void apply(IndicatorValue oldValue, IndicatorValue value) {
        if (value == null) {
            logger.warn("Indicator value is null!");
            return;
        }

        JSONArray jsonArray = (JSONArray) JSONArray.toJSON(value.value);
        if (jsonArray == null || jsonArray.size() == 0) {
            logger.warn("{}---Indicator value is empty!", value.entityId);
            return;
        } else {
            logger.debug("value--->{},entityId--->{}", jsonArray.toString(), value.entityId);
        }
        JSONArray historyArray = null;
        if (oldValue != null) {
            historyArray = (JSONArray) JSONArray.toJSON(oldValue.value);
            logger.debug("oldValue --->{},entityId--->{}", oldValue.value.toString(), oldValue.entityId);
        } else {
            logger.info("oldValue is null");
        }
        if (historyArray == null || historyArray.size() == 0) {
            for (int i = 0; i < jsonArray.size(); i++) {
                InterfaceStatus if_status = calculateIfStatus(jsonArray.getJSONObject(i));
                jsonArray.getJSONObject(i).put("if_status", if_status.toString());
                JSONObject obj = jsonArray.getJSONObject(i);
                if ("nullnull".equals(obj.getString("if_in_pkts"))) {
                    obj.remove("if_in_pkts");
                }
                if ("nullnull".equals(obj.getString("if_out_pkts"))) {
                    obj.remove("if_out_pkts");
                }
                if (obj.getString("if_out_in_pkts_sum") != null && obj.getString("if_out_in_pkts_sum").contains("null")) {
                    obj.remove("if_out_in_pkts_sum");
                }
                if (obj.getString("if_out_in_errors_sum") != null && obj.getString("if_out_in_errors_sum").contains("null")) {
                    obj.put("if_out_in_errors_sum", null);
                }
                if (obj.getString("if_out_in_discards_sum") != null && obj.getString("if_out_in_discards_sum").contains("null")) {
                    obj.put("if_out_in_discards_sum", null);
                }
            }
        } else {
            Long beginTime = oldValue.queryTimeMillis;
            Long endTime = value.queryTimeMillis;
            Double runTime = ArithUtils.div((endTime - beginTime), 1000);

            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject json = jsonArray.getJSONObject(i);
                String newStatus = calculateIfStatus(jsonArray.getJSONObject(i)).toString();
                jsonArray.getJSONObject(i).put("if_status", newStatus);
                for (int r = 0; r < historyArray.size(); r++) {
                    JSONObject historyValue = historyArray.getJSONObject(r);
                    if (historyValue.getString("if_descr") != null && json.getString("if_descr") != null
                            && json.getString("if_descr").equals(historyValue.getString("if_descr"))) {
                        Long in_number = sub(json.getLongValue("if_in_octets"),
                            historyValue.getLongValue("if_in_octets"));
                        if (json.containsKey("if_hc_in_octets") && json.get("if_hc_in_octets") != null
                                && historyValue.containsKey("if_hc_in_octets")
                                && historyValue.get("if_hc_in_octets") != null) {
                            in_number = sub(json.getLongValue("if_hc_in_octets"),
                                historyValue.getLongValue("if_hc_in_octets"));
                        }
                        int in = 2;
                        while (in_number < 0 && in-- > 0) {
                            in_number = (long) (in_number + Math.pow(2, 32));
                        }

                        if (runTime < MIN_INTERVAL && in_number == 0) {
                            in_number = historyValue.getLongValue("if_in_number");
                        }
                        Long out_number = sub(json.getLongValue("if_out_octets"),
                            historyValue.getLongValue("if_out_octets"));
                        if (json.containsKey("if_hc_out_octets") && json.get("if_hc_out_octets") != null
                                && historyValue.containsKey("if_hc_out_octets")
                                && historyValue.get("if_hc_out_octets") != null) {
                            out_number = sub(json.getLongValue("if_hc_out_octets"),
                                historyValue.getLongValue("if_hc_out_octets"));
                        }
                        int out = 2;
                        if (out_number < 0 && out-- > 0) {
                            out_number = (long) (out_number + Math.pow(2, 32));
                        }
                        if (runTime < MIN_INTERVAL && out_number == 0) {
                            out_number = historyValue.getLongValue("if_out_number");
                        }
                        jsonArray.getJSONObject(i).put("if_in_number", in_number);
                        jsonArray.getJSONObject(i).put("if_in_speed", speed(in_number, runTime));

                        jsonArray.getJSONObject(i).put("if_out_number", out_number);
                        jsonArray.getJSONObject(i).put("if_out_speed", speed(out_number, runTime));
                        Long inDiscards = sub(json.getLongValue("if_in_discards"),
                            historyValue.getLongValue("if_in_discards"));
                        Long outDiscards = sub(json.getLongValue("if_out_discards"),
                            historyValue.getLongValue("if_out_discards"));
                        Long inErrors = sub(json.getLongValue("if_in_errors"),
                            historyValue.getLongValue("if_in_errors"));
                        Long outErrors = sub(json.getLongValue("if_out_errors"),
                            historyValue.getLongValue("if_out_errors"));
                        Long in_total = null;
                        if ("nullnull".equals(json.getString("if_in_pkts"))) {
                            json.remove("if_in_pkts");
                        }
                        if ("nullnull".equals(json.getString("if_out_pkts"))) {
                            json.remove("if_out_pkts");
                        }
                        if (json.getString("if_out_in_errors_sum") != null && json.getString("if_out_in_errors_sum").contains("null")) {
                            json.put("if_out_in_errors_sum", null);
                        }
                        if (json.getString("if_out_in_discards_sum") != null && json.getString("if_out_in_discards_sum").contains("null")) {
                            json.put("if_out_in_discards_sum", null);
                        }
                        if (json.getString("if_in_pkts") != null && !json.getString("if_in_pkts").contains("null")
                                && historyValue.getString("if_in_pkts") != null
                                && !historyValue.getString("if_in_pkts").contains("null")) {
                            try {

                                in_total = sub(json.getLongValue("if_in_pkts"),
                                    historyValue.getLongValue("if_in_pkts"));
                            } catch (Exception e) {
                                logger.error("calculate in_total error", e);
                            }
                        }
                        Long out_total = null;
                        if (json.getString("if_out_pkts") != null && !json.getString("if_out_pkts").contains("null")
                                && historyValue.getString("if_out_pkts") != null
                                && !historyValue.getString("if_out_pkts").contains("null")) {
                            try {
                                out_total = sub(json.getLongValue("if_out_pkts"),
                                    historyValue.getLongValue("if_out_pkts"));
                            } catch (Exception e) {
                                logger.error("calculate out_total error", e);
                            }
                        }
                        if (runTime < MIN_INTERVAL && in_total != null && in_total == 0) {
                            jsonArray.getJSONObject(i).put("if_in_discards_percent",
                                historyValue.getLongValue("if_in_discards_percent"));
                            jsonArray.getJSONObject(i).put("if_in_errors_percent",
                                historyValue.getLongValue("if_in_errors_percent"));
                        } else {
                            jsonArray.getJSONObject(i).put("if_in_discards_percent", usage(inDiscards, in_total));
                            jsonArray.getJSONObject(i).put("if_in_errors_percent", usage(inErrors, in_total));
                        }
                        if (runTime < MIN_INTERVAL && out_total != null && out_total == 0) {
                            jsonArray.getJSONObject(i).put("if_out_discards_percent",
                                historyValue.getLongValue("if_out_discards_percent"));
                            jsonArray.getJSONObject(i).put("if_out_errors_percent",
                                historyValue.getLongValue("if_out_errors_percent"));
                        } else {
                            jsonArray.getJSONObject(i).put("if_out_discards_percent", usage(outDiscards, out_total));
                            jsonArray.getJSONObject(i).put("if_out_errors_percent", usage(outErrors, out_total));
                        }
                        // 计算总的丢包率
                        if (json.getString("if_out_in_pkts_sum") != null
                                && json.getString("if_out_in_discards_sum") != null
                                && !json.getString("if_out_in_pkts_sum").contains("null")
                                && !json.getString("if_out_in_discards_sum").contains("null")) {
                            jsonArray.getJSONObject(i).put("if_out_in_sum_percent", usage(
                                json.getLongValue("if_out_in_discards_sum"), json.getLongValue("if_out_in_pkts_sum")));
                        } else {
                            json.remove("if_out_in_pkts_sum");
                            json.remove("if_out_in_discards_sum");
                        }
                        Double if_speed = json.getDoubleValue("if_speed");
                        String if_working_mode = json.getString("if_working_mode");
                        if (if_speed != 0L) {
                            jsonArray.getJSONObject(i).put("if_in_utilization",
                                utilization(in_number, runTime, if_speed));
                            jsonArray.getJSONObject(i).put("if_out_utilization",
                                utilization(out_number, runTime, if_speed));
                            jsonArray.getJSONObject(i).put("if_total_utilization",
                                totalUtilization(in_number, out_number, runTime, if_speed, if_working_mode));
                        } else if (runTime < MIN_INTERVAL) {
                            jsonArray.getJSONObject(i).put("if_in_utilization",
                                historyValue.getLongValue("if_in_utilization"));
                            jsonArray.getJSONObject(i).put("if_out_utilization",
                                historyValue.getLongValue("if_out_utilization"));
                            jsonArray.getJSONObject(i).put("if_total_utilization",
                                historyValue.getLongValue("if_total_utilization"));
                        }
                        break;
                    }
                }
            }
        }
        value.value = jsonArray;
    }

    public InterfaceStatus calculateIfStatus(Map<String, Object> ifEntry) {
        JSONObject json = new JSONObject(ifEntry);
        return calculateIfStatus(json);
    }

    /**
     * 计算接口整体状态
     */
    private InterfaceStatus calculateIfStatus(JSONObject json) {
        if (json.containsKey("if_status") && StringUtils.isNoneBlank(json.getString("if_status"))) {
            try {
                return InterfaceStatus.valueOf(json.getString("if_status"));
            } catch (Exception e) {
                logger.error("if_status 无法识别：[{}]", json.getString("if_status"));
            }
        }
        if (json.containsKey("if_admin_status")) {
            if (StringUtils.isBlank(json.getString("if_admin_status"))) {
                return InterfaceStatus.down;
            }
            if (StringUtils.isNumeric(json.getString("if_admin_status"))) {
                if (json.getInteger("if_admin_status") > 3) {
                    logger.warn("接口状态数据if_admin_status无法识别:{}", json.getString("if_admin_status"));
                    return InterfaceStatus.unknown;
                }
                if (json.getInteger("if_admin_status") > 1) {
                    return InterfaceStatus.fromValue(json.getInteger("if_admin_status"));
                }
            } else if (!"UP".equalsIgnoreCase(json.getString("if_admin_status"))) {
                return InterfaceStatus.down;
            }

        }
        if (!json.containsKey("if_oper_status")) {
            logger.warn("接口未采集到if_oper_status状态数据:{}", json.toJSONString());
            return InterfaceStatus.unknown;
        } else if (StringUtils.isBlank(json.getString("if_oper_status"))) {
            return InterfaceStatus.down;
        }
        if (StringUtils.isNumeric(json.getString("if_oper_status"))) {
            int operStatus = Integer.parseInt(json.getString("if_oper_status"));
            if (operStatus == 2) {
                return InterfaceStatus.notUsed;
            }
            try {
                return InterfaceStatus.fromValue(json.getInteger("if_oper_status"));
            } catch (Exception e) {
                logger.warn("接口状态数据if_oper_status无法识别:{}", json.getString("if_oper_status"));
            }
            return InterfaceStatus.unknown;
        } else if ("RUNNING".equalsIgnoreCase(json.getString("if_oper_status"))) {
            return InterfaceStatus.up;
        } else {
            return InterfaceStatus.down;
        }
    }

    /**
     * 
     * @param total
     *            1 octet = 1 byte
     * @return Mbits
     */
    @SuppressWarnings("unused")
    private Double number(Long total) {
        // return ArithUtils.div(total * size, size_unit * size_unit);
        return ArithUtils.mul(total, SIZE);
    }

    /**
     * 
     * @param total
     *            1 octet = 1 byte
     * @param time
     *            1 s
     * @return Mbits/s
     */
    private Double speed(Long total, Double time) {
        if (new Double(0).equals(time)) {
            return 0D;
        }
        return ArithUtils.div(total, time);
    }

    private Double utilization(Long total, Double time, Double speed) {
        if (ArithUtils.mul(time, speed) != 0d) {
            return ArithUtils.div(total * PERCENT / 1000 / 1000, ArithUtils.mul(time, speed));
        } else {
            return 0d;
        }

    }

    // in %
    private Double totalUtilization(Long inNumber, Long outNumber, Double time, Double speed) {
        if (time == 0.0D || speed == 0.0D) {
            return null;
        }
        return Math.max(inNumber, outNumber) * 8.0 / 1000.0 / 1000.0 / time / speed * 100.0;
    }

    /**
    * @Author Liuxh
    * @Description //计算网络带宽利用率
    * @Date 13:46 2020/3/26
    * @Param [inNumber, outNumber, time, speed, workingMode--网卡工作模式（1=未知、2=半双工、3=全双工）]
    * @return java.lang.Double
    **/
    private Double totalUtilization(Long inNumber, Long outNumber, Double time, Double speed, String workingMode) {
        if (time == 0.0D || speed == 0.0D) {
            return null;
        }
        if ("2".equals(workingMode)) {
            // 半双工
            return (inNumber + outNumber) * 8.0 / 1000 / 1000 / time / speed * 100.0;
        } else {
            // 全双工
            return Math.max(inNumber, outNumber) * 8.0 / 1000 / 1000 / time / speed * 100.0;
        }
    }

    private Long sub(Long current, Long old) {
        if (current < old) {
            current = current + MAXVALUE_32;
        }
        return current - old;
    }

    private Double usage(Long used, Long total) {
        if (total != null && total != 0L) {
            Double usage = ArithUtils.div(used * PERCENT, total);
            if (usage >= PERCENT) {
                logger.info("used大于等于total：{}>={}", used, total);
                return 100d;
            } else {
                return usage;
            }
        } else {
            return null;
        }
    }
}
